package com.ming.liteflow.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.ivy.parser.IvyFlowParser;
import com.ivy.parser.execption.LiteFlowELException;
import com.ming.common.Options;
import com.ming.common.SortBy;
import com.ming.common.beetl.entity.BaseEntity;
import com.ming.common.beetl.entity.DataSourceEntity;
import com.ming.common.beetl.entity.SQLManagerEntity;
import com.ming.common.beetl.enums.DictEnums;
import com.ming.common.beetl.util.Result;
import com.ming.common.beetl.util.SqlUtil;
import com.ming.common.beetl.util.StrUtil;
import com.ming.common.liteflow.core.chain.IvyChain;
import com.ming.common.liteflow.core.config.IvyConfig;
import com.ming.common.liteflow.core.el.IvyEl;
import com.ming.common.liteflow.core.flowexecutor.IvyExecutor;
import com.ming.common.liteflow.core.node.IvyCmp;
import com.ming.common.liteflow.vo.IvyChainVo;
import com.ming.common.xxljob.annotation.PermissionLimit;
import com.zaxxer.hikari.HikariDataSource;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.page.PageResult;
import org.beetl.sql.core.query.LambdaQuery;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/liteflow/chain")
public class LiteFlowChainController {

    @Resource
    private SQLManager sqlManager;

    @PostMapping("/options")
    @PermissionLimit(limit = false)
    public Result<?> options(@RequestBody(required = false) Map<String,Object> map) {
        LambdaQuery<IvyChain> query = sqlManager.lambdaQuery(IvyChain.class);
        List<IvyChain> list = query.select(IvyChain::getId,IvyChain::getChainId,IvyChain::getChainName);
        list = list.stream().peek(m->m.setChainName(m.getChainName()+"【"+m.getChainId()+"】")).collect(Collectors.toList());
        return Result.OK(list);
    }

    @PostMapping("/page")
    @PermissionLimit(limit = false)
    public Result<PageResult<IvyChain>> page(@RequestBody IvyChainVo vo){
        LambdaQuery<IvyChain> lambdaQuery = sqlManager.lambdaQuery(IvyChain.class);
        lambdaQuery.andEq(IvyChain::getIvyElId, LambdaQuery.filterEmpty(vo.getIvyElId()));
        lambdaQuery.andLike(IvyChain::getChainId, LambdaQuery.filterLikeEmpty(vo.getChainId()));
        lambdaQuery.andLike(IvyChain::getChainName, LambdaQuery.filterLikeEmpty(vo.getChainName()));
        Options options = vo.getOptions();
        List<SortBy> sortBy = options.getSortBy();
        for (SortBy sort : sortBy) {
            if ("desc".equalsIgnoreCase(sort.getOrder())) {
                lambdaQuery.desc(StrUtil.camelToSnake(sort.getKey()));
            } else {
                lambdaQuery.asc(StrUtil.camelToSnake(sort.getKey()));
            }
        }
        PageResult<IvyChain> page = lambdaQuery.page(options.getPage(), options.getItemsPerPage());
        return Result.OK(page);
    }

    @PostMapping("/add")
    @PermissionLimit(limit = false)
    public Result<?> add(@RequestBody IvyChain item){
        LambdaQuery<IvyChain> query = sqlManager.lambdaQuery(IvyChain.class);
        int i = query.insert(item);
        return Result.OK(i);
    }

    @PostMapping("/update")
    @PermissionLimit(limit = false)
    public Result<Object> update(@RequestBody IvyChain item){
        LambdaQuery<IvyChain> lambdaQuery = sqlManager.lambdaQuery(IvyChain.class);
        lambdaQuery.andNotEq(IvyChain::getId, item.getId());
        lambdaQuery.andEq(IvyChain::getChainId, item.getChainId());
        long count = lambdaQuery.count();
        if(count > 0){
            return Result.error("链路ID重复");
        }
        int i = sqlManager.updateById(item);
        return Result.OK("更新成功", i);
    }

    @PostMapping("/delete")
    @PermissionLimit(limit = false)
    public Result<Integer> delete(@RequestBody IvyChain item){
        LambdaQuery<IvyChain> lambdaQuery = sqlManager.lambdaQuery(IvyChain.class);
        lambdaQuery.andEq(IvyChain::getId, item.getId());
        int i = lambdaQuery.delete();
        return Result.OK("删除成功",i);
    }

    @PostMapping("/sync")
    @PermissionLimit(limit = false)
    public Result<Integer> sync(@RequestBody IvyChain item) throws InstantiationException, IllegalAccessException, LiteFlowELException {
        IvyChain ivyChain = sqlManager.lambdaQuery(IvyChain.class).andEq(IvyChain::getId, item.getId()).single();
        IvyEl ivyEl = sqlManager.lambdaQuery(IvyEl.class).andEq(IvyEl::getId, ivyChain.getIvyElId()).single();
        IvyExecutor ivyExecutor = sqlManager.lambdaQuery(IvyExecutor.class).andEq(IvyExecutor::getId, ivyEl.getExecutorId()).single();
        IvyConfig ivyConfig = sqlManager.lambdaQuery(IvyConfig.class).andEq(IvyConfig::getId, ivyExecutor.getIvyConfigId()).single();
        if(ivyConfig != null){
            String ruleType = ivyConfig.getRuleType();
            Integer configType = ivyConfig.getConfigType();
            // 如果是Mysql数据库配置源
            if("ruleSourceSql".equals(ruleType)){
                String configJson = null;
                if(configType != null && configType == 1){
                    configJson = ivyConfig.getRuleSourceExtDataMap();
                }else if(configType != null && configType == 2){
                    configJson = ivyConfig.getRuleSourceExtData();
                }
                if(cn.hutool.core.util.StrUtil.isNotBlank(configJson)){
                    JSONObject configMap = JSONUtil.parseObj(configJson);
                    String chainTableName = configMap.getStr("chainTableName");
                    String url = configMap.getStr("url");
                    String driverClassName = configMap.getStr("driverClassName");
                    String username = configMap.getStr("username");
                    String password = configMap.getStr("password");

                    HikariDataSource hikariDataSource = SqlUtil.buildHikariDataSource(new DataSourceEntity(driverClassName,url,username,password));
                    SQLManagerEntity entity = new SQLManagerEntity();
                    entity.setDataSource(hikariDataSource);
                    entity.setDbStyle(DictEnums.DbStyle.getDbStyle("mysql"));
                    SQLManager manager = SqlUtil.buildSQLManager(entity);

                    // 链路表同步
                    Class<? extends BaseEntity> dynamicEntity = SqlUtil.dynamicEntity(manager, chainTableName);
                    int delete = manager.lambdaQuery(dynamicEntity)
                            .andEq(configMap.getStr("chainApplicationNameField"), configMap.get("applicationName"))
                            .andEq(configMap.getStr("chainNameField"), ivyChain.getChainId())
                            .delete();
                    BaseEntity obj = dynamicEntity.newInstance();
                    obj.setValue(StrUtil.snakeToCamel(configMap.getStr("chainApplicationNameField")),configMap.get("applicationName"));
                    obj.setValue(StrUtil.snakeToCamel(configMap.getStr("chainNameField")),ivyChain.getChainId());
                    obj.setValue(StrUtil.snakeToCamel(configMap.getStr("elDataField")),ivyEl.getEl());
                    manager.insertTemplate(obj);

                    Set<JsonNode> scriptNodes = IvyFlowParser.getScriptNodes(ivyEl.getSourceJson());
                    if(CollUtil.isNotEmpty(scriptNodes)){
                        Set<Long> nodeIdSet = scriptNodes.stream().map(m->Long.parseLong(m.get("id").asText())).collect(Collectors.toSet());
                        Map<Long, IvyCmp> ivyCmpMap = sqlManager.lambdaQuery(IvyCmp.class).andIn(IvyCmp::getId, nodeIdSet).select().stream().collect(Collectors.toMap(IvyCmp::getId, m -> m));

                        // 脚本表同步
                        Class<? extends BaseEntity> scriptEntity = SqlUtil.dynamicEntity(manager, configMap.getStr("scriptTableName"));
                        for (JsonNode scriptNode : scriptNodes){
                            Long id = Long.parseLong(scriptNode.get("id").asText());
                            IvyCmp ivyCmp = ivyCmpMap.get(id);

                            String nodeId = scriptNode.get("componentId").asText();
                            String nodeName = scriptNode.get("componentName").asText();
                            String scriptContent = scriptNode.get("script").asText();
                            String scriptType = scriptNode.get("type").asText();
                            int delete1 = manager.lambdaQuery(scriptEntity)
                                    .andEq(configMap.getStr("chainApplicationNameField"), configMap.get("applicationName"))
                                    .andEq(configMap.getStr("scriptIdField"), nodeId)
                                    .delete();
                            BaseEntity script = scriptEntity.newInstance();
                            script.setValue(StrUtil.snakeToCamel(configMap.getStr("chainApplicationNameField")),configMap.get("applicationName"));
                            script.setValue(StrUtil.snakeToCamel(configMap.getStr("scriptIdField")), nodeId);
                            script.setValue(StrUtil.snakeToCamel(configMap.getStr("scriptNameField")), nodeName);
                            script.setValue(StrUtil.snakeToCamel(configMap.getStr("scriptDataField")), scriptContent);
                            script.setValue(StrUtil.snakeToCamel(configMap.getStr("scriptTypeField")), scriptType);
                            script.setValue(StrUtil.snakeToCamel(configMap.getStr("scriptLanguageField")), ivyCmp != null ? ivyCmp.getLanguage() : "java");
                            manager.insertTemplate(script);
                        }
                    }
                }
            }
        }
        return Result.OK();
    }
}
